using UnityEngine;
using System.Collections;
 
[RequireComponent (typeof (Rigidbody))]
[RequireComponent (typeof (CapsuleCollider))]
 
public class RigidbodyFPSController : MonoBehaviour {
 	public Transform fpsCamera;
	public Camera buildCamera;
	
	public float featherWeightEffect	= 0.9f;
	public float weightSpeedEffect		= 0.5f;
	
	public MiniMap minimap;
	
	public int maxHP = 100;
	private int HP;
	public float weight = 1;
	public float aimSpread = 1;
	private float latestAimSpread;
	public int damage = 10;
	public float bashStrength = 2.0f;
	
	public float speed = 10.0f;
	public float gravity = 10.0f;
	public float maxVelocityChange = 1.0f;
	public bool canJump = true;
	public float jumpHeight = 2.0f;
	private bool grounded = false;
 
 	public string moveAxisX = "Horizontal";
	public string moveAxisY = "Vertical";
	public string jumpButton = "Jump";
	
	//startpoint = -0.1;
	public float stepsize = 0.3f;
	

	public float slideLimit = 45f;
	public float slideSpeed = 0.5f;
	public float maxSlope = 75f;
	
//	private Vector3 contactPoint;
	private RaycastHit hit;
	
	private int feathers = 0;
	private int spuds = 0;
	private int eyes	= 0;
	
	public float scopeSpreadEffect = 0.75f;
	
	
	private int scope	= 0;
	private int basemaxHP;
	private float baseweight;
	private float baseaimSpread;
	private float basespeed;
	private int basedamage;
	
	//private float rayDistance;
	public float minimumGunCD = 0.1f;
	public float maximumGunCD = 3f;
	public float weightGunCDEffect = 0.1f;
	public float gunCD = 1.0f;
	private float basegunCD;
	private float bashCD;
	private bool sliding = false;
	
	void Awake () {
	    rigidbody.freezeRotation = true;
	    rigidbody.useGravity = false;//false
		
	}
	
	void Start () {
		//Sets the basevalues for resetting
		basegunCD 		= gunCD; 
		basemaxHP 		= maxHP;
		baseweight 		= weight;
		baseaimSpread 	= aimSpread;
		basespeed 		= speed;
		basedamage 		= damage;
		
		
		if(gameObject.GetComponent<WeaponSystem>() != null){
			gameObject.GetComponent<WeaponSystem>().SetNewSpread(aimSpread);
		}
		
		latestAimSpread = aimSpread;
		HP = maxHP;
		
		if(gameObject.GetComponent<WeaponSystem>() != null){
			gameObject.GetComponent<WeaponSystem>().SetNewSpread(aimSpread);
		}
		
		
		if(gameObject.GetComponent<Damagable>() != null){
			//Tells the damagable scrip about its new maxHP
			gameObject.GetComponent<Damagable>().ChangeMaxHP(maxHP);
			gameObject.GetComponent<Damagable>().ChangeHP(HP);
		}
		
	}
	
	void FixedUpdate () {
		
		if(latestAimSpread != aimSpread){
			Debug.Log("Aimspread changed, I'll go ahead and tell the weaponsystem about the change.");
			latestAimSpread = aimSpread;
			if(gameObject.GetComponent<WeaponSystem>() != null){
				gameObject.GetComponent<WeaponSystem>().SetNewSpread(aimSpread);
			}
		}
		
		//Movement
	    if (grounded) {
			Vector3 targetVelocity;
			
			if ((buildCamera == null || !buildCamera.enabled)) {
				targetVelocity = new Vector3(Input.GetAxis(moveAxisX), 0, Input.GetAxis(moveAxisY));
			} else {
				targetVelocity = Vector3.zero;
			}
			
			targetVelocity.Normalize();
			
	        targetVelocity = fpsCamera.TransformDirection(targetVelocity);
	        targetVelocity *= speed;
			
			if (sliding) {	
                Vector3 hitNormal = hit.normal;
                Vector3 slideVelocity = new Vector3(hitNormal.x, -hitNormal.y, hitNormal.z);
                //Vector3.OrthoNormalize (ref hitNormal, ref slideVelocity);
				
                slideVelocity 	*= slideSpeed;
				targetVelocity 	+= slideVelocity;
            }
			
	        // Apply a force that attempts to reach our target velocity
	        Vector3 velocity = rigidbody.velocity;
	        Vector3 velocityChange = (targetVelocity - velocity);
	        velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
	        velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
	        velocityChange.y = 0;
			
	        rigidbody.AddForce(velocityChange, ForceMode.VelocityChange);
 			
	        // Jump
	        if ((buildCamera == null || !buildCamera.enabled) && canJump && Input.GetButton(jumpButton)) {
	            rigidbody.velocity = new Vector3(velocity.x, velocity.y + CalculateJumpVerticalSpeed(), velocity.z);
				grounded = false;
				
				networkView.RPC("rpcJump", RPCMode.All, networkView.viewID);
	        }
			
			sliding = false;
	    }
 
	    // We apply gravity manually for more tuning control
	    rigidbody.AddForce(new Vector3 (0, -gravity * rigidbody.mass, 0));
 
	    grounded = false;
	}
	
	private bool isNearZero(float val) {
		return Mathf.Abs(val) < 0.001f;
	}
 
	void OnCollisionStay (Collision collisionInfo) {
	
	    //grounded = true;
		
//		 foreach (ContactPoint contact in collisionInfo.contacts) {
//			contactPoint = contact.point;
            //Debug.DrawRay(contact.point, contact.normal, Color.white);
//        }
		
		// Check Slope			
			// If slope is more than maxSlope or the distance to the ground is greater than stepsize it calculates velocity as if it had no contact with the ground. 

		Ray ray = new Ray(transform.position+new Vector3(0,0.1f,0), -Vector3.up);
   		RaycastHit hit = new RaycastHit();
	
        //if (Physics.Raycast(myTransform.position-new Vector3(0,100,0), -Vector3.up, out hit, stepsize)) {
		Debug.DrawRay(transform.position+new Vector3(0,0.1f,0),-Vector3.up*stepsize,Color.red);
		
		if (Physics.Raycast(ray, out hit, stepsize, 1)){
			grounded = true;

            if (Vector3.Angle(hit.normal, Vector3.up) > slideLimit) {
                sliding 	= true;
			}

			if (Vector3.Angle(hit.normal, Vector3.up) > maxSlope){
				Debug.Log("Bigger than maxSlope");
				grounded = false;
			}
			
        } else {
			grounded = false;
		}
		
		if (grounded) {
			networkView.RPC("rpcGrounded", RPCMode.All, networkView.viewID);
		}
	}
	
	[RPC]
	public void rpcJump(NetworkViewID playerID) {
		NetworkView view = NetworkView.Find(playerID);
		if(gameObject.GetComponent<WeaponSystem>().enabled);
			Listeners.inst().notify(Channel.ON_JUMP, view.GetComponent<PlayerState>());
	}
	
	[RPC]
	public void rpcGrounded(NetworkViewID playerID) {
		NetworkView view = NetworkView.Find(playerID);
		Listeners.inst().notify(Channel.ON_GROUNDED, view.GetComponent<PlayerState>());
	}
 
	public void finalUpdateStats() {
		for (int i = 0; i < feathers; i++){
			weight *= featherWeightEffect;
		}
		
		if (bashCD > Time.time) {
			weight *= bashStrength;
		}
		
		for (int i = 0; i < scope; i++){
			aimSpread *= scopeSpreadEffect;
		}
		speed = Mathf.Clamp(speed-weight*weightSpeedEffect,0,100);
		gunCD = Mathf.Clamp(gunCD+weight*weightGunCDEffect,minimumGunCD,maximumGunCD);
		if (this.GetComponent<WeaponSystem>() != null)
			this.GetComponent<WeaponSystem>().setGunCD(gunCD);
		
		
	}
	
	float CalculateJumpVerticalSpeed () {
	    // From the jump height and gravity we deduce the upwards speed 
	    // for the character to reach at the apex.
	    return Mathf.Sqrt(2 * jumpHeight * gravity);
	}
	[RPC]
	public int GetHP()					{return maxHP; }
	[RPC]
	public float GetSpeed() 			{return speed; }
	[RPC]
	public float GetWeight() 			{return weight;}
	[RPC]
	public float GetAim()				{return aimSpread;}
	[RPC]
	public int GetDamage()				{return damage;}
	[RPC]
	public int GetFeathers()				{return feathers;}
	[RPC]
	public float GetGunCD()				{return gunCD;}
	[RPC]
	public int GetScope()				{return scope;}
	
	public int GetSpuds()				{return spuds;}
	
	[RPC]
	public void AddMaxHP(int added)	{
		if(gameObject.GetComponent<Damagable>() != null){
			maxHP = maxHP + added;
			basemaxHP = basemaxHP + added;
			//Tells the damagable scrip about its new maxHP
			gameObject.GetComponent<Damagable>().ChangeMaxHP(maxHP);
		}
	} 
	[RPC]
	public void AddHP(int added) {
		if(gameObject.GetComponent<Damagable>() != null)
			HP = Mathf.Min(HP+added,maxHP);
			gameObject.GetComponent<Damagable>().ChangeHP(Mathf.Min(HP+added,maxHP));
			
	}
	[RPC]
	public void AddSpeed(float change)	{speed 		= speed + change;}
	[RPC]
	public void AddWeight(float change)	{weight 	= weight + change;}
	[RPC]
	public void AddAim(float change)	{aimSpread 	= aimSpread * change;}
	[RPC]
	public void AddDamage(int change)	{damage 	= damage + change;}
	[RPC]
	public void AddFeather(int change)	{feathers 	= feathers + change;}
	[RPC]
	public void AddGunCD(float change)	{gunCD 	= gunCD + change;}
	[RPC]
	public void AddScope(int change)	{scope 	= scope + change;}
	[RPC]
	public void AddSpud(int change)		{spuds 	= spuds + change;}
	
	public void AddBash(float cd) 		{bashCD = Mathf.Max(Time.time + cd, bashCD + cd);}
	public void AddEye(int eye)			{eyes += eye; minimap.setEyes(eyes);}
	
	[RPC]
	public void ResetStats(){
		maxHP 		= basemaxHP;
		weight 		= baseweight;
		aimSpread 	= baseaimSpread;
		speed 		= basespeed;
		damage 		= basedamage;
		feathers	= 0;
		gunCD		= basegunCD;
		bashCD		= 0;
		scope		= 0;
		spuds		= 0;
		eyes		= 0;
		minimap.setEyes(0);
		Debug.Log ("Reset the stats, will fetch the new stats.");
	}
	
	

	
}